feat: foundational support for programmatic tool calling#11508
feat: foundational support for programmatic tool calling#11508roomote[bot] wants to merge 1 commit intomainfrom
Conversation
- Add supportsProgrammaticToolCalling flag to ModelInfo schema - Add enableProgrammaticToolCalling setting to GlobalSettings - Set the flag on supported Anthropic models (Claude Sonnet 4.5, Opus 4.x) - Add ApiStreamCodeExecutionChunk type to the API stream - Create programmatic-tool-calling service with: - DockerSandboxExecutor for isolated Python code execution - ToolBridge for generating Python SDK with tool function stubs - IPC protocol for tool call communication between sandbox and host - Support for read_file, write_to_file, execute_command, search_files, list_files - Add comprehensive tests (27 passing) Addresses #11506
Reviewed the foundational programmatic tool calling architecture. Found 3 issues in
Mention @roomote in a comment to request specific changes to this pull request or fix all unresolved issues. |
| "--read-only", | ||
| "--security-opt=no-new-privileges", |
There was a problem hiding this comment.
--read-only makes the entire root filesystem read-only, and the workspace is mounted :ro. This leaves Python with no writable directory at all. Python itself handles missing __pycache__ gracefully, but any user code (or library) that uses tempfile, writes to /tmp, or needs a writable home directory will fail with a read-only filesystem error. The standard pattern for read-only Docker containers is to add --tmpfs /tmp so there's a writable scratch space.
| "--read-only", | |
| "--security-opt=no-new-privileges", | |
| "--read-only", | |
| "--tmpfs", "/tmp", | |
| "--security-opt=no-new-privileges", |
Fix it with Roo Code or mention @roomote and request a fix.
| case "complete": | ||
| // Code execution completed - handled in the close event | ||
| break |
There was a problem hiding this comment.
The "complete" case in the switch is a no-op break, so the success and error fields from the Python script's completion message are silently discarded. The close handler only picks them up if the complete message happens to be the last incomplete fragment in stdoutBuffer (i.e., not terminated by \n). In the normal case, the Python SDK writes the complete message with a trailing newline, so it arrives as a full line, gets parsed here, and is thrown away. The close handler then falls through to exitCode === 0 and never sees the structured error or traceback from the sandbox.
Consider storing the parsed complete message (e.g., in a local variable) so the close handler can use its success/error fields instead of relying on the exit code.
Fix it with Roo Code or mention @roomote and request a fix.
| for (const line of lines) { | ||
| if (!line.trim()) { | ||
| continue | ||
| } | ||
| this.handleSandboxMessage(line, proc, toolCalls).catch((err) => { | ||
| stderr += `IPC error: ${err.message}\n` | ||
| }) | ||
| } |
There was a problem hiding this comment.
handleSandboxMessage is async (it awaits onToolApproval and onToolExecute) but is called fire-and-forget here with only a .catch(). If the timeout fires while a tool approval/execution is in-flight, the promise resolves and proc is killed, but handleSandboxMessage continues running and will attempt proc.stdin!.write() on a dead process. The .catch() swallows the error, but the tool call result being pushed to toolCalls at that point is lost since the outer promise already resolved. Consider tracking in-flight tool requests so the timeout handler can wait for or abort them cleanly, or at minimum guard the stdin.write against a closed/killed process.
Fix it with Roo Code or mention @roomote and request a fix.
Related GitHub Issue
Closes: #11506
Description
This PR attempts to address Issue #11506 by laying the foundational architecture for programmatic tool calling. Feedback and guidance are welcome.
What this PR adds:
Type system extensions:
supportsProgrammaticToolCallingflag onModelInfoschema to detect model capabilityenableProgrammaticToolCallingtoggle onGlobalSettingsfor user opt-inApiStreamCodeExecutionChunkstream type for code execution blocksModel capability detection:
supportsProgrammaticToolCalling: trueDocker-based sandbox executor (
DockerSandboxExecutor):Python tool SDK (
ToolBridge):read_file,write_to_file,execute_command,search_files,list_filesComprehensive tests: 27 tests across 3 test files covering types, ToolBridge, and DockerSandboxExecutor
Design decisions:
supportsProgrammaticToolCallingon ModelInfo means any provider can opt inStill needed (future PRs):
Test Procedure
Tests were run from the
srcworkspace:Result: 3 test files passed, 27 tests passed.
All lint and type-check passes verified via pre-push hook (turbo lint + turbo check-types).
Pre-Submission Checklist
Documentation Updates
This is foundational architecture that is not yet user-facing. Documentation will be needed once the feature is fully integrated in subsequent PRs.
Start a new Roo Code Cloud session on this branch